#include "RayTracing.h"

int const RayTracing::EXIT_CODE_REBOOT = -123456789;

World mainWorld;

RayTracing::RayTracing(QWidget* parent)
	: QMainWindow(parent)
{
	ui.setupUi(this);

	view = QPixmap(RESOLUTION, RESOLUTION);
	initDefaultVar();

	onActionCreateSphere();

	ui.objList->setProperty("contextMenuPolicy", Qt::CustomContextMenu);
	ui.objList->setSelectionMode(QAbstractItemView::ExtendedSelection);

	objMenu = new QMenu(this);
	addObjMenu = new QMenu(tr("Add Object"), this);
	addSphereAction = new QAction(tr("Sphere"), this);
	addTriangleAction = new QAction(tr("Triangle"), this);
	editObjAction = new QAction(tr("Edit Object"), this);
	delObjAction = new QAction(tr("Delete Object"), this);
	renameObjAction = new QAction(tr("Rename Object"), this);
	addObjMenu->addAction(addSphereAction);
	addObjMenu->addAction(addTriangleAction);
	objMenu->addMenu(addObjMenu);
	objMenu->addAction(editObjAction);
	objMenu->addAction(renameObjAction);
	objMenu->addAction(delObjAction);

	connect(ui.objList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onObjListMenuRequested(const QPoint&)));
	connect(addSphereAction, SIGNAL(triggered()), this, SLOT(onActionCreateSphere()));
	connect(addTriangleAction, SIGNAL(triggered()), this, SLOT(onActionCreateTriangle()));
	connect(editObjAction, SIGNAL(triggered()), this, SLOT(onActionEdit()));
	connect(delObjAction, SIGNAL(triggered()), this, SLOT(onActionDelete()));
	connect(renameObjAction, SIGNAL(triggered()), this, SLOT(onActionRename()));
	connect(ui.objList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(onActionEdit(QListWidgetItem*)));
	connect(ui.camx, SIGNAL(valueChanged(double)), this, SLOT(onCameraUpdate(double)));
	connect(ui.camy, SIGNAL(valueChanged(double)), this, SLOT(onCameraUpdate(double)));
	connect(ui.camz, SIGNAL(valueChanged(double)), this, SLOT(onCameraUpdate(double)));
	connect(ui.tarx, SIGNAL(valueChanged(double)), this, SLOT(onCameraUpdate(double)));
	connect(ui.tary, SIGNAL(valueChanged(double)), this, SLOT(onCameraUpdate(double)));
	connect(ui.tarz, SIGNAL(valueChanged(double)), this, SLOT(onCameraUpdate(double)));
	connect(ui.fovBox, SIGNAL(valueChanged(double)), this, SLOT(onCameraUpdate(double)));
	connect(ui.rendButton, QOverload<bool>::of(&QPushButton::clicked), this, [=](bool) {
		this->setCursor(Qt::WaitCursor);
		view = rend(mainWorld, ui.sampleTimesBox->value(), ui.rayLengthBox->value(), ui.progressBar);
		ui.view->setPixmap(view);
		this->setCursor(Qt::ArrowCursor);
		});

	connect(ui.actionSave, SIGNAL(triggered()), this, SLOT(onSaveFile()));
	connect(ui.actionOpen, SIGNAL(triggered()), this, SLOT(onOpenFile()));
	connect(ui.actionNew, SIGNAL(triggered()), this, SLOT(onNewFile()));
	connect(ui.actionExport, SIGNAL(triggered()), this, SLOT(onExport()));

	view = rend(mainWorld, 4, 4, ui.progressBar);
}

void RayTracing::onObjListMenuRequested(const QPoint& pos)
{
	int count = ui.objList->selectedItems().count();
	if (count < 1)
	{
		delObjAction->setEnabled(false);
		editObjAction->setEnabled(false);
	}
	else if (count != 1) {
		delObjAction->setEnabled(true);
		editObjAction->setEnabled(false);
	}
	else
	{
		delObjAction->setEnabled(true);
		editObjAction->setEnabled(true);
	}

	objMenu->exec(QCursor::pos());
}

void RayTracing::onActionCreateSphere()
{
	obj* newObj = new Sphere(tr("New Sphere"), vec(5, 0, 0), 1);
	QListWidgetItem* newitem = new QListWidgetItem(newObj->name);
	mainWorld.objlist.append(newObj);
	objMap.insert(newitem, newObj);
	ui.objList->addItem(newitem);
	view = rend(mainWorld, 4, 4, ui.progressBar);
	ui.view->setPixmap(view);

}

void RayTracing::onActionCreateTriangle()
{
	obj* newObj = new Triangle(tr("New Triangle"), vec(1, 1, 1), vec(1, 1, -1), vec(1, -1, -1));
	QListWidgetItem* newitem = new QListWidgetItem(newObj->name);
	mainWorld.objlist.append(newObj);
	objMap.insert(newitem, newObj);
	ui.objList->addItem(newitem);
	view = rend(mainWorld, 4, 4, ui.progressBar);
	ui.view->setPixmap(view);

}

void RayTracing::onActionDelete()
{
	QList<QListWidgetItem*> items = ui.objList->selectedItems();
	if (items.count() > 0)
	{
		QApplication::beep();
		if (QMessageBox::Yes == QMessageBox::question(this, QString(tr("Remove Item")),
			QString(tr("Remove %1 item")).arg(QString::number(items.count())), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes))
		{
			foreach(QListWidgetItem * var, items) {
				ui.objList->removeItemWidget(var);
				mainWorld.objlist.removeOne(objMap[var]);
				objMap.remove(var);
				items.removeOne(var);
				delete var;
			}
			view = rend(mainWorld, 4, 4, ui.progressBar);
			ui.view->setPixmap(view);
		}
	}
}

void RayTracing::onActionEdit()
{
	QList<QListWidgetItem*> items = ui.objList->selectedItems();
	if (items.count() == 1) {
		switch (objMap[ui.objList->currentItem()]->type)
		{
		case objType::Tri:
			triDia = new TriangleSettingDialog((Triangle*)objMap[ui.objList->currentItem()], this);
			triDia->exec();
			delete triDia;
			break;
		case objType::Sph:
			sphDia = new SphereSettingDialog((Sphere*)objMap[ui.objList->currentItem()], this);
			sphDia->exec();
			delete sphDia;
			break;
		default:
			break;
		}
		view = rend(mainWorld, 4, 4, ui.progressBar);
		ui.view->setPixmap(view);

	}
}
void RayTracing::onActionEdit(QListWidgetItem* item)
{
	onActionEdit();
}

void RayTracing::onActionRename()
{
	if (ui.objList->currentItem() != NULL)
	{
		QString dlgTitle = tr("Rename Object");
		QString txtLabel = tr("Please enter the name of the object");
		QString defaultInput = objMap.value(ui.objList->currentItem())->name;
		QLineEdit::EchoMode echoMode = QLineEdit::Normal;
		bool ok = false;
		QString text = QInputDialog::getText(this, dlgTitle, txtLabel, echoMode, defaultInput, &ok);
		if (ok && !text.isEmpty())
		{
			objMap.value(ui.objList->currentItem())->name = text;
			ui.objList->currentItem()->setText(text);
		}

	}
}

void RayTracing::onCameraUpdate(double)
{
	mainWorld.cam.org.setX(ui.camx->value());
	mainWorld.cam.org.setY(ui.camy->value());
	mainWorld.cam.org.setZ(ui.camz->value());
	mainWorld.cam.target.setX(ui.tarx->value());
	mainWorld.cam.target.setY(ui.tary->value());
	mainWorld.cam.target.setZ(ui.tarz->value());
	mainWorld.cam.fov = ui.fovBox->value();
	view = rend(mainWorld, 4, 4, ui.progressBar);
	ui.view->setPixmap(view);

}

void RayTracing::onSaveFile()
{
	QFileDialog fd;
	QString dir = fd.getSaveFileName(this, tr("Save File"), "", tr("Json Files (*.json)"));
	if (!dir.isNull())
	{
		FileManager(dir).Save();
	}
}

void RayTracing::onOpenFile()
{
	QFileDialog fd;
	QString dir = fd.getOpenFileName(this, tr("Open File"), "", tr("Json Files (*.json)"));
	if (!dir.isNull())
	{
		FileManager(dir).Open();

		ui.fovBox->setValue(mainWorld.cam.fov);
		ui.camx->setValue(mainWorld.cam.org.x());
		ui.camy->setValue(mainWorld.cam.org.y());
		ui.camz->setValue(mainWorld.cam.org.z());
		ui.tarx->setValue(mainWorld.cam.target.x());
		ui.tary->setValue(mainWorld.cam.target.y());
		ui.tarz->setValue(mainWorld.cam.target.z());


		ui.objList->clear();
		objMap.clear();

		for each (obj * var in mainWorld.objlist)
		{
			QListWidgetItem* newItem = new QListWidgetItem(var->name);
			objMap.insert(newItem, var);
			ui.objList->addItem(newItem);
		}

		view = rend(mainWorld, 4, 4, ui.progressBar);
		ui.view->setPixmap(view);

	}
}

void RayTracing::onNewFile()
{
	QString program = QApplication::applicationFilePath();
	QStringList arguments = QApplication::arguments();
	QString workingDirectory = QDir::currentPath();
	QProcess::startDetached(program, arguments, workingDirectory);
	QApplication::exit();
}

void RayTracing::onExport()
{
	QFileDialog fd;
	QString dir = fd.getSaveFileName(this, tr("Export Image"), "", tr("PNG image (*.png)"));
	if (!dir.isNull())
	{
		view.save(dir, NULL);
	}
}